﻿using System;
using UnityEngine;

namespace Core
{
    public static partial class Utils
    {
        public static void ForDir2(this Vector2Int center, Action<Vector2Int> func)
        {
            Dir.For2(center, func);
        }

        public static void ForDir4(this Vector2Int center, Action<Vector2Int> func)
        {
            Dir.For4(center, func);
        }

        public static void ForDir8(this Vector2Int center, Action<Vector2Int> func)
        {
            Dir.For8(center, func);
        }

        public static void ForDir8(this Vector2Int center, Func<Vector2Int, bool> func)
        {
            Dir.For8(center, func);
        }

        public static void ForDir8(this Vector2Int center, Action<Vector2Int, int> func)
        {
            Dir.For8(center, func);
        }

        public static class Dir
        {
            public static readonly Vector2Int[] Dir8Arr =
            {
                new(-1, 1), new(0, 1), new(1, 1),
                new(-1, 0), new(1, 0),
                new(-1, -1), new(0, -1), new(1, -1),
            };

            public static readonly Vector2Int[] Dir4Arr =
            {
                new(0, 1),
                new(-1, 0),
                new(1, 0),
                new(0, -1),
            };

            public static EDir2 Get2(Vector2 vec)
            {
                var dir = vec.x > 0 ? EDir2.Right : EDir2.Left;
                return dir;
            }

            public static EDir2 Get2(float angle)
            {
                var dir = angle switch
                {
                    >= 0 and < 180 => EDir2.Right,
                    >= 180 and < 360 => EDir2.Left,
                    _ => EDir2.Invalid
                };
                return dir;
            }

            public static EDir2 Get2(string str)
            {
                EDir2 dir = EDir2.Invalid;
                str = str.ToLower();
                dir = str switch
                {
                    "left" => EDir2.Left,
                    "right" => EDir2.Right,
                    _ => dir
                };
                return dir;
            }

            public static EDir4 Get4(Vector2 vec)
            {
                EDir4 dir;
                if (Mathf.Abs(vec.y) >= Mathf.Abs(vec.x))
                {
                    dir = vec.y > 0 ? EDir4.Up : EDir4.Down;
                }
                else
                {
                    dir = vec.x > 0 ? EDir4.Right : EDir4.Left;
                }

                return dir;
            }

            public static EDir4 Get4(float angle)
            {
                var dir = angle switch
                {
                    >= 0 and < 40 or >= 320 and < 360 => EDir4.Up,
                    >= 50 and < 130 => EDir4.Right,
                    >= 140 and < 220 => EDir4.Down,
                    >= 230 and < 310 => EDir4.Left,
                    _ => EDir4.Invalid
                };
                return dir;
            }

            public static EDir4 Get4(string str)
            {
                EDir4 dir = EDir4.Invalid;
                str = str.ToLower();
                dir = str switch
                {
                    "up" => EDir4.Up,
                    "down" => EDir4.Down,
                    "left" => EDir4.Left,
                    "right" => EDir4.Right,
                    _ => dir
                };
                return dir;
            }

            public static Vector2Int Get4(EDir4 dir)
            {
                var vec = dir switch
                {
                    EDir4.Up => Vector2Int.up,
                    EDir4.Down => Vector2Int.down,
                    EDir4.Left => Vector2Int.left,
                    EDir4.Right => Vector2Int.right,
                    _ => Vector2Int.zero
                };
                return vec;
            }


            public static EDir4 Get8(float angle)
            {
                var dir = angle switch
                {
                    >= 0 and < 40 or >= 320 and < 360 => EDir4.Up,
                    >= 50 and < 130 => EDir4.Right,
                    >= 140 and < 220 => EDir4.Down,
                    >= 230 and < 310 => EDir4.Left,
                    _ => EDir4.Invalid
                };
                return dir;
            }

            public static void For2(Action<Vector2Int> func)
            {
                if (func == null) return;

                func(Vector2Int.left);
                func(Vector2Int.right);
            }

            public static void For2(Vector2Int center, Action<Vector2Int> func)
            {
                if (func == null) return;

                func(center.Plus(1, 0));
                func(center.Plus(-1, 0));
            }

            public static void For4(Action<Vector2Int> func)
            {
                if (func == null) return;

                func(Vector2Int.up);
                func(Vector2Int.down);
                func(Vector2Int.left);
                func(Vector2Int.right);
            }

            public static void For4(Vector2Int center, Action<Vector2Int> func)
            {
                if (func == null) return;

                func(center.Plus(0, 1));
                func(center.Plus(0, -1));
                func(center.Plus(-1, 0));
                func(center.Plus(1, 0));
            }

            public static void For8(Action<Vector2Int> func)
            {
                if (func == null) return;

                func(new Vector2Int(-1, 1));
                func(new Vector2Int(0, 1));
                func(new Vector2Int(1, 1));
                func(new Vector2Int(-1, 0));
                func(new Vector2Int(1, 0));
                func(new Vector2Int(-1, -1));
                func(new Vector2Int(0, -1));
                func(new Vector2Int(1, -1));
            }

            public static void For8(Vector2Int center, Action<Vector2Int> func)
            {
                if (func == null) return;

                func(center.Plus(-1, 1));
                func(center.Plus(0, 1));
                func(center.Plus(1, 1));
                func(center.Plus(-1, 0));
                func(center.Plus(1, 0));
                func(center.Plus(-1, -1));
                func(center.Plus(0, -1));
                func(center.Plus(1, -1));
            }

            public static void For9(Vector2Int center, Action<Vector2Int> func)
            {
                if (func == null) return;

                func(center.Plus(-1, 1));
                func(center.Plus(0, 1));
                func(center.Plus(1, 1));
                func(center.Plus(-1, 0));
                func(center.Plus(0, 0));
                func(center.Plus(1, 0));
                func(center.Plus(-1, -1));
                func(center.Plus(0, -1));
                func(center.Plus(1, -1));
            }

            public static void For8(Vector2Int center, Func<Vector2Int, bool> func)
            {
                if (func == null) return;

                if (!func(center.Plus(-1, 1))) return;
                if (!func(center.Plus(0, 1))) return;
                if (!func(center.Plus(1, 1))) return;
                if (!func(center.Plus(-1, 0))) return;
                if (!func(center.Plus(1, 0))) return;
                if (!func(center.Plus(-1, -1))) return;
                if (!func(center.Plus(0, -1))) return;
                if (!func(center.Plus(1, -1))) return;
            }

            public static void For8(Vector2Int center, Action<Vector2Int, int> func)
            {
                if (func == null) return;

                func(center.Plus(-1, 1), 0);
                func(center.Plus(0, 1), 1);
                func(center.Plus(1, 1), 2);
                func(center.Plus(-1, 0), 3);
                func(center.Plus(1, 0), 4);
                func(center.Plus(-1, -1), 5);
                func(center.Plus(0, -1), 6);
                func(center.Plus(1, -1), 7);
            }
        }
    }
}